#include <string.h>
#include <stdio.h>
#include <pthread.h>
#include <sys/select.h>
#include <sys/prctl.h>
#include "../include/hi_mw_media_intf.h"
#include "hi_comm_aio.h"
#include "mpi_audio.h"
#include "mpi_sys.h"
#include "app_common.h"

#define MAX_AENC_START_CNT (5)

extern HI_HANDLE g_hACapHdl;
static pthread_mutex_t s_AEncLock = PTHREAD_MUTEX_INITIALIZER;

typedef struct tagAEncStartAttr_S
{
    HI_U32 u32StartCnt;
    HI_MW_OnAEncDataCB_FN apfnDataCB[MAX_AENC_START_CNT];
    HI_VOID *apPrivData[MAX_AENC_START_CNT];
    pthread_t pthGetStreamThd;
    HI_BOOL  bThreadRun;
    HI_HANDLE hAencHdl;
}AEncStartAttr_S;

static AEncStartAttr_S astAEncStartAttrs[] = 
{
    {0, {NULL,}, {NULL,}, 0, HI_FALSE, 0},
    {0, {NULL,}, {NULL,}, 0, HI_FALSE, 0},
    {0, {NULL,}, {NULL,}, 0, HI_FALSE, 0},
    {0, {NULL,}, {NULL,}, 0, HI_FALSE, 0},
    {0, {NULL,}, {NULL,}, 0, HI_FALSE, 0},
};

static HI_S32 AEncDataProc(HI_HANDLE hAEncHdl, AUDIO_STREAM_S* pAStreamData, __attribute__((unused)) HI_VOID* pPrivateData)
{
    HI_MW_MEDIA_AUDIO_S stAudioInfo = {0, 0, 0, 0, 0};
    stAudioInfo.pu8Addr = pAStreamData->pStream;
    stAudioInfo.u64PhyAddr = pAStreamData->u64PhyAddr;
    stAudioInfo.u32Len = pAStreamData->u32Len;
    stAudioInfo.u64TimeStamp = pAStreamData->u64TimeStamp;
    stAudioInfo.u32Seq = pAStreamData->u32Seq;
    HI_U32 i;
    for (i = 0; i < astAEncStartAttrs[hAEncHdl].u32StartCnt; i++)
    {
        astAEncStartAttrs[hAEncHdl].apfnDataCB[i](hAEncHdl, &stAudioInfo, astAEncStartAttrs[hAEncHdl].apPrivData[i]);
    }
    return 0;
}

HI_VOID* MW_Adapt_GetAencStreamProc(HI_VOID* p)
{
    HI_S32 s32Ret = HI_FAILURE;
    fd_set read_fds;
    struct timeval TimeoutVal;
    AUDIO_STREAM_S stStream;
    AEncStartAttr_S* pstAEncAttr = (AEncStartAttr_S*)p;
    AENC_CHN AeChn = pstAEncAttr->hAencHdl;
    HI_CHAR aszThreadName[64];
    snprintf(aszThreadName, 64, "%s_%d", "MW_Adapt_GetAencStreamProc",pstAEncAttr->hAencHdl);
    prctl(PR_SET_NAME, aszThreadName, 0, 0, 0);
    HI_S32 s32AencFd = HI_MPI_AENC_GetFd(AeChn);
    if (s32AencFd < 0)
    {
        LOGE("HI_MPI_AENC_GetFd failed ret %#x\n",s32AencFd);
    }
    memset(&stStream, 0, sizeof(stStream));
    while (HI_TRUE == pstAEncAttr->bThreadRun)
    {
        FD_ZERO(&read_fds);
        FD_SET(s32AencFd, &read_fds);
        TimeoutVal.tv_sec = 1;
        TimeoutVal.tv_usec = 0;
        s32Ret = select(s32AencFd + 1, &read_fds, NULL, NULL, &TimeoutVal);
        if (s32Ret < 0)
        {
            LOGE("select failed!\n");
            break;
        }
        else if (s32Ret == 0)
        {
            //SAMPLE_PRT("get venc %d stream time out, exit thread\n",VeChn);
            continue;
        }
        else
        {
            if (FD_ISSET(s32AencFd, &read_fds))
            {
                //step 2.2 : call mpi to get one-frame stream
                s32Ret = HI_MPI_AENC_GetStream(AeChn, &stStream, HI_TRUE);
                if (HI_SUCCESS != s32Ret)
                {
                    LOGE("HI_MPI_AENC_GetStream failed with %#x!\n", \
                           s32Ret);
                    break;
                }

                //step 2.5 : proc frame
               AEncDataProc(pstAEncAttr->hAencHdl, &stStream, (HI_VOID *)pstAEncAttr);
                //step 2.6 : release stream
                s32Ret = HI_MPI_AENC_ReleaseStream(AeChn, &stStream);
                if (HI_SUCCESS != s32Ret)
                {
                    LOGE("HI_MPI_AENC_ReleaseStream failed with %#x!\n", s32Ret);
                    break;
                }

            }

        }
    }
    return NULL;
}

HI_S32 MW_Adapt_Aenc_StartGetStream(HI_HANDLE  hAencHdl)
{
    HI_S32 s32Ret = HI_SUCCESS;
    astAEncStartAttrs[hAencHdl].bThreadRun = HI_TRUE;
    astAEncStartAttrs[hAencHdl].hAencHdl = hAencHdl;
    s32Ret = pthread_create(&astAEncStartAttrs[hAencHdl].pthGetStreamThd, 0, MW_Adapt_GetAencStreamProc, (HI_VOID*) & (astAEncStartAttrs[hAencHdl]));
    return s32Ret;
}

HI_S32 MW_Adapt_Aenc_StopGetStream(HI_HANDLE  hAencHdl)
{
    HI_S32 s32Ret = HI_SUCCESS;
    astAEncStartAttrs[hAencHdl].bThreadRun = HI_FALSE;
    pthread_join(astAEncStartAttrs[hAencHdl].pthGetStreamThd, HI_NULL);
    return s32Ret;
}

HI_S32 HI_MW_AEnc_Start(HI_HANDLE hAEncHdl, HI_MW_OnAEncDataCB_FN pfnDataCB, HI_VOID *pPrivData)
{
    HI_S32 s32Ret = HI_SUCCESS;
    MPP_CHN_S stSrcChn,stDestChn;
    pthread_mutex_lock(&s_AEncLock);
    HI_U32 u32StartCnt = astAEncStartAttrs[hAEncHdl].u32StartCnt;
    if (MAX_AENC_START_CNT-1 == u32StartCnt)
    {
        LOGW("aenc start too many times");
        pthread_mutex_unlock(&s_AEncLock);
        return HI_FAILURE;
    }
    astAEncStartAttrs[hAEncHdl].apfnDataCB[u32StartCnt] = pfnDataCB;
    astAEncStartAttrs[hAEncHdl].apPrivData[u32StartCnt] = pPrivData;
    if (0 == u32StartCnt)
    {
        //todo start aenc and get stream.
        stSrcChn.enModId = HI_ID_AI;
        stSrcChn.s32DevId = g_hACapHdl;
        stSrcChn.s32ChnId = g_hACapHdl;
        stDestChn.enModId = HI_ID_AENC;
        stDestChn.s32DevId = 0;
        stDestChn.s32ChnId = hAEncHdl;
        s32Ret = HI_MPI_SYS_Bind(&stSrcChn, &stDestChn);
        if(HI_SUCCESS != s32Ret)
        {
            LOGE("HI_MPI_SYS_Bind failed ret %#x\n",s32Ret);
            return s32Ret;
        }
        MW_Adapt_Aenc_StartGetStream(hAEncHdl);
    }
    astAEncStartAttrs[hAEncHdl].u32StartCnt++;
    pthread_mutex_unlock(&s_AEncLock);
    return HI_SUCCESS;
}

HI_S32 HI_MW_AEnc_Stop(HI_HANDLE hAEncHdl, HI_MW_OnAEncDataCB_FN pfnDataCB)
{
    MPP_CHN_S stSrcChn,stDestChn;
    pthread_mutex_lock(&s_AEncLock);
    HI_U32 u32StartCnt = astAEncStartAttrs[hAEncHdl].u32StartCnt;
    HI_U32 i = 0, j = 0;
    for (; i < u32StartCnt; i++)
    {
        if (pfnDataCB == astAEncStartAttrs[hAEncHdl].apfnDataCB[i])
        {
            break;
        }
    }
    if (i == MAX_AENC_START_CNT)
    {
        LOGW("not fount stop aenc");
        pthread_mutex_unlock(&s_AEncLock);
        return HI_FAILURE;
    }

    for (j = i; j < u32StartCnt; j++)
    {
        astAEncStartAttrs[hAEncHdl].apfnDataCB[j] = astAEncStartAttrs[hAEncHdl].apfnDataCB[j+1];
        astAEncStartAttrs[hAEncHdl].apPrivData[j] = astAEncStartAttrs[hAEncHdl].apPrivData[j+1];
    }
    if (0 == --astAEncStartAttrs[hAEncHdl].u32StartCnt)
    {
        //stop aenc and get stream.
        MW_Adapt_Aenc_StopGetStream(hAEncHdl);
        stSrcChn.enModId = HI_ID_AI;
        stSrcChn.s32DevId = g_hACapHdl;
        stSrcChn.s32ChnId = g_hACapHdl;
        stDestChn.enModId = HI_ID_AENC;
        stDestChn.s32DevId = 0;
        stDestChn.s32ChnId = hAEncHdl;
        HI_MPI_SYS_UnBind(&stSrcChn, &stDestChn);
    }
    pthread_mutex_unlock(&s_AEncLock);
    return HI_SUCCESS;
}
